#ifndef TCP_SERVER_LOGSTREAM_H
#define TCP_SERVER_LOGSTREAM_H

#include "../noncopyable.h"
#include "../Types.h"
#include "../../net/Buffer.h"
#include <cassert>
#include <cstring> // memcpy

namespace detail {

    const int kSmallBuffer = 4000;
    const int kLargeBuffer = 4000 * 1000;

    template<int SIZE>
    class FixedBuffer : muduo::noncopyable {
    public:
        FixedBuffer() : cur_(data_) {
            setCookie(cookieStart);
        }

        ~FixedBuffer() {
            setCookie(cookieEnd);
        }

        void append(const char * /*restrict*/ buf, size_t len) {
            // FIXME: append partially
            if (implicit_cast<size_t>(avail()) > len) {
                memcpy(cur_, buf, len);
                cur_ += len;
            }
        }

        const char *data() const { return data_; }

        int length() const { return static_cast<int>(cur_ - data_); }

        // write to data_ directly
        char *current() { return cur_; }

        /**
         * 剩余有效的空间长度
         * @return
         */
        int avail() const { return static_cast<int>(end() - cur_); }

        void add(size_t len) { cur_ += len; }

        void reset() { cur_ = data_; }

        void bzero() { memZero(data_, sizeof data_); }

        // for used by GDB
        const char *debugString();

        void setCookie(void (*cookie)()) { cookie_ = cookie; }

        // for used by unit test
        string toString() const { return string(data_, length()); }

        // FIXME:这个可能有问题
        StringArg toStringArg() const { return StringArg(toString()); }

    private:
        const char *end() const { return data_ + sizeof data_; }

        // Must be outline function for cookies.
        static void cookieStart();

        static void cookieEnd();

        void (*cookie_)();

        char data_[SIZE];
        // 存储的数据
        char *cur_;
        // 当前指向的末尾指针
    };

}  // namespace detail

/**
 * 日志流
 */
class LogStream : muduo::noncopyable {
    typedef LogStream self;
public:
    typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;

    LogStream &operator<<(bool v) {
        buffer_.append(v ? "1" : "0", 1);
        return *this;
    }

    LogStream &operator<<(short);

    LogStream &operator<<(unsigned short);

    LogStream &operator<<(int);

    LogStream &operator<<(unsigned int);

    LogStream &operator<<(long);

    LogStream &operator<<(unsigned long);

    LogStream &operator<<(long long);

    LogStream &operator<<(unsigned long long);

    LogStream &operator<<(const void *);

    LogStream &operator<<(float v) {
        *this << static_cast<double>(v);
        return *this;
    }

    LogStream &operator<<(double);

    // self& operator<<(long double);
    LogStream &operator<<(char v) {
        buffer_.append(&v, 1);
        return *this;
    }

    // self& operator<<(signed char);
    // self& operator<<(unsigned char);

    LogStream &operator<<(const char *str) {
        if (str) {
            buffer_.append(str, strlen(str));
        } else {
            buffer_.append("(null)", 6);
        }
        return *this;
    }

    LogStream &operator<<(const unsigned char *str) {
        return operator<<(reinterpret_cast<const char *>(str));
    }

    LogStream &operator<<(const string &v) {
        buffer_.append(v.c_str(), v.size());
        return *this;
    }

    LogStream &operator<<(const StringArg &v) {
        buffer_.append(v.data(), v.size());
        return *this;
    }

    LogStream &operator<<(const Buffer &v) {
        *this << v.data();
        return *this;
    }

    void append(const char *data, int len) { buffer_.append(data, len); }

    const Buffer &buffer() const { return buffer_; }

    void resetBuffer() { buffer_.reset(); }

private:
    void staticCheck();

    template<typename T>
    void formatInteger(T);

    Buffer buffer_;

    static const int kMaxNumericSize = 32;
};

class Fmt // : noncopyable
{
public:
    template<typename T>
    Fmt(const char *fmt, T val);

    const char *data() const { return buf_; }

    int length() const { return length_; }

private:
    char buf_[32];
    int length_;
};

inline LogStream &operator<<(LogStream &s, const Fmt &fmt) {
    s.append(fmt.data(), fmt.length());
    return s;
}

// Format quantity n in SI units (k, M, G, T, P, E).
// The returned string is atmost 5 characters long.
// Requires n >= 0
string formatSI(int64_t n);

// Format quantity n in IEC (binary) units (Ki, Mi, Gi, Ti, Pi, Ei).
// The returned string is atmost 6 characters long.
// Requires n >= 0
string formatIEC(int64_t n);


#endif //TCP_SERVER_LOGSTREAM_H
